package com.example.elasticsearchdemo.dao;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;


public class ResultMapper implements SearchResultMapper {

    //方法表
    private static final Map<String, Field> FIELD_MAP = new HashMap<>();

    private static ResultMapper resultMapper = new ResultMapper();

    public static ResultMapper getInstance() {
        return resultMapper;
    }

    private ResultMapper() {
    }

    @Override
    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

        //返回的结果
        ArrayList<T> ts = new ArrayList<>();

        //匹配项
        SearchHits hits = response.getHits();
        for (SearchHit searchHit : hits) {
            try {
                //利用反射将生成结果
                T instance = clazz.newInstance();
                Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();

                //首先将结果映射为一个对象
                searchHit.getSourceAsMap().forEach((f, v) -> {
                    assign(clazz, instance, f, v);
                });

                //之后再将高亮的字段替换.
                searchHit.getHighlightFields().forEach((f, v) -> {
                    assign(clazz, instance, f, v.fragments()[0].string());
                });

                ts.add(instance);
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }

        }
        return new AggregatedPageImpl<>(ts, pageable, ts.size());
    }

    private void assign(Class<?> clazz, Object clazzObj, String field, Object value) {

        if (value == null) {
            return;
        }

        try {

            //field是高亮字段
            Field f = null;

            //type是当前的字段类型
            Class<?> type = clazz;

            //obj是当前字段对象
            Object obj = clazzObj;

            //字段的持有者
            Object fObj = obj;


                for (String fld : field.split("\\.")) {
                    f = field(type, fld);
                    type = f.getType();
                    fObj = obj;
                    Object o = f.get(fObj);
                    if (o == null) {
                        o = type.newInstance();
                        f.set(obj, o);
                    }
                    obj = o;
                }


            if (type == String.class) {
                f.set(fObj, value);
            } else {
                Map<String, Object> map = (Map<String, Object>) value;
                Class<?> finalType = type;
                Object finalObj = obj;
                map.forEach((k, v) -> {
                    assign(finalType, finalObj, k, v);
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //找到指定class的指定字段.如果在当前的类中找不到就递归去父类中寻找.直至找到Object为止
    private Field field(Class<?> clazz, String f) {
        if (clazz == Object.class) {
            return null;
        }
        String fieldName = clazz.getName() + "." + f;
        Field field = FIELD_MAP.get(fieldName);
        if (field == null) {
            try {
                field = clazz.getDeclaredField(f);

                //如果能够找到就将该字段放入map中
                field.setAccessible(true);
                FIELD_MAP.put(fieldName, field);
            } catch (NoSuchFieldException e) {//如果在当前类中找不到就去父类中查找
                field = field(clazz.getSuperclass(), f);
            }
        }
        return field;
    }

}
